﻿using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Lightbug.Utilities
{

    [System.AttributeUsage(System.AttributeTargets.Field)]
    public class ConditionAttribute : PropertyAttribute
    {
        public enum ConditionType
        {
            IsTrue,
            IsFalse,
            IsGreaterThan,
            IsEqualTo,
            IsLessThan,
            IsNotNull,
            IsNull,
        }

        public enum VisibilityType
        {
            Hidden,
            NotEditable
        }

        public string[] conditionPropertyNames;
        public ConditionType[] conditionTypes;
        public float[] values;
        public VisibilityType visibilityType;


        /// <summary>
        /// This attribute will determine the visibility of the target property based on some other property condition. Use this attribute if the target property 
        /// depends on some other property inside the class.
        /// </summary>
        /// <param name="conditionPropertyName">Name of the property used by the condition.</param>
        /// <param name="conditionType">The condition type.</param>
        /// <param name="visibilityType">The visibility action to perform if the condition is not met.</param>
        /// <param name="conditionValue">The condition argument value.</param>
        public ConditionAttribute(string conditionPropertyName, ConditionType conditionType, VisibilityType visibilityType = VisibilityType.Hidden, float conditionValue = 0f)
        {
            this.conditionPropertyNames = new string[] { conditionPropertyName };
            this.conditionTypes = new ConditionType[] { conditionType };
            this.visibilityType = visibilityType;
            this.values = new float[] { conditionValue };

        }

        public ConditionAttribute(string[] conditionPropertyNames, ConditionType[] conditionTypes, float[] conditionValues, VisibilityType visibilityType = VisibilityType.Hidden)
        {
            this.conditionPropertyNames = conditionPropertyNames;
            this.conditionTypes = conditionTypes;
            this.visibilityType = visibilityType;
            this.values = conditionValues;
        }
    }

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(ConditionAttribute))]
    public class ConditionAttributeEditor : PropertyDrawer
    {
        ConditionAttribute target;


        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            target ??= attribute as ConditionAttribute;

            bool result = CheckCondition(property);
            if (target.visibilityType == ConditionAttribute.VisibilityType.NotEditable)
            {
                GUI.enabled = result;
                EditorGUI.PropertyField(position, property, label, true);
                GUI.enabled = true;
            }
            else
            {
                if (result)
                    EditorGUI.PropertyField(position, property, label, true);
            }


        }

        bool result = false;

        bool CheckCondition(SerializedProperty property)
        {
            bool output = true;
            for (int i = 0; i < target.conditionPropertyNames.Length; i++)
            {
                output &= EvaluateCondition(property, target.conditionPropertyNames[i], target.conditionTypes[i], target.values[i]);
            }

            return output;
        }

        bool EvaluateCondition(SerializedProperty property, string conditionPropertyName, ConditionAttribute.ConditionType conditionType, float value)
        {
            SerializedProperty conditionProperty = property.serializedObject.FindProperty(conditionPropertyName);

            // if the "conditionProperty" is null, then the property is probably part of a plain C# serialized class. If so, then find the property root path
            // and look for the target condition property again.
            if (conditionProperty == null)
            {
                string propertyPath = property.propertyPath;
                int lastIndex = propertyPath.LastIndexOf('.');

                if (lastIndex == -1)
                    return true;


                string propertyParentPath = propertyPath.Substring(0, lastIndex);

                conditionProperty = property.serializedObject.FindProperty(propertyParentPath).FindPropertyRelative(conditionPropertyName);

                if (conditionProperty == null)
                    return true;

            }


            result = false;

            SerializedPropertyType conditionPropertyType = conditionProperty.propertyType;

            if (conditionPropertyType == SerializedPropertyType.Boolean)
            {
                if (conditionType == ConditionAttribute.ConditionType.IsTrue)
                    result = conditionProperty.boolValue;
                else if (conditionType == ConditionAttribute.ConditionType.IsFalse)
                    result = !conditionProperty.boolValue;

            }
            else if (conditionPropertyType == SerializedPropertyType.Float)
            {

                float conditionPropertyFloatValue = conditionProperty.floatValue;
                float argumentFloatValue = value;

                switch (conditionType)
                {
                    case ConditionAttribute.ConditionType.IsTrue:
                        result = conditionPropertyFloatValue != 0f;
                        break;
                    case ConditionAttribute.ConditionType.IsFalse:
                        result = conditionPropertyFloatValue == 0f;
                        break;
                    case ConditionAttribute.ConditionType.IsGreaterThan:
                        result = conditionPropertyFloatValue > argumentFloatValue;
                        break;
                    case ConditionAttribute.ConditionType.IsEqualTo:
                        result = conditionPropertyFloatValue == argumentFloatValue;
                        break;
                    case ConditionAttribute.ConditionType.IsLessThan:
                        result = conditionPropertyFloatValue < argumentFloatValue;
                        break;
                }

            }
            else if (conditionPropertyType == SerializedPropertyType.Integer || conditionPropertyType == SerializedPropertyType.Enum)
            {
                int conditionPropertyIntValue = conditionProperty.intValue;
                int argumentIntValue = (int)value;

                switch (conditionType)
                {
                    case ConditionAttribute.ConditionType.IsTrue:
                        result = conditionPropertyIntValue != 0;
                        break;
                    case ConditionAttribute.ConditionType.IsFalse:
                        result = conditionPropertyIntValue == 0;
                        break;
                    case ConditionAttribute.ConditionType.IsGreaterThan:
                        result = conditionPropertyIntValue > argumentIntValue;
                        break;
                    case ConditionAttribute.ConditionType.IsEqualTo:
                        result = conditionPropertyIntValue == argumentIntValue;
                        break;
                    case ConditionAttribute.ConditionType.IsLessThan:
                        result = conditionPropertyIntValue < argumentIntValue;
                        break;
                }

            }
            else if (conditionPropertyType == SerializedPropertyType.ObjectReference)
            {
                UnityEngine.Object conditionPropertyObjectValue = conditionProperty.objectReferenceValue;

                switch (conditionType)
                {
                    case ConditionAttribute.ConditionType.IsNull:
                        result = conditionPropertyObjectValue == null;
                        break;
                    case ConditionAttribute.ConditionType.IsNotNull:
                        result = conditionPropertyObjectValue != null;
                        break;
                }


            }

            return result;
        }

        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            target ??= attribute as ConditionAttribute;

            return !result && target.visibilityType == ConditionAttribute.VisibilityType.Hidden ? 0f : EditorGUI.GetPropertyHeight(property);
        }



    }

#endif

}

